package com.ssyx.activity.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssyx.activity.feign.ProductFeignClient;
import com.ssyx.activity.mapper.ActivityInfoMapper;
import com.ssyx.activity.mapper.ActivityRuleMapper;
import com.ssyx.activity.mapper.ActivitySkuMapper;
import com.ssyx.activity.mapper.CouponInfoMapper;
import com.ssyx.activity.service.ActivityInfoService;
import com.ssyx.activity.service.CouponInfoService;
import com.ssyx.enums.ActivityType;
import com.ssyx.model.activity.ActivityInfo;
import com.ssyx.model.activity.ActivityRule;
import com.ssyx.model.activity.ActivitySku;
import com.ssyx.model.activity.CouponInfo;
import com.ssyx.model.order.CartInfo;
import com.ssyx.model.product.SkuInfo;
import com.ssyx.vo.activity.ActivityRuleVo;
import com.ssyx.vo.order.CartInfoVo;
import com.ssyx.vo.order.OrderConfirmVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * ClassName:ActivityInfoServiceImpl
 * Description:
 *
 * @Author kay
 * @Create 2024/5/5 23:24
 * @Version 1.0
 */
@Service
public class ActivityInfoServiceImpl extends ServiceImpl<ActivityInfoMapper, ActivityInfo>
                implements ActivityInfoService {

    @Autowired
    private ActivityRuleMapper activityRuleMapper;

    @Autowired
    private ActivitySkuMapper activitySkuMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CouponInfoMapper couponInfoMapper;

    @Autowired
    private CouponInfoService couponInfoService;

    //活动分页列表
    @Override
    public IPage<ActivityInfo> getPage(Page<ActivityInfo> pageParams) {
        IPage<ActivityInfo> activityInfoPage = baseMapper.selectPage(pageParams, null);

        //分页查询对象里面,获取列表数据
        List<ActivityInfo> activityInfoList = activityInfoPage.getRecords();
        //遍历activityInfoList集合,得到每个ActivityInfo对象
        //向ActivityInfo对象封装活动类型到activityTypeString属性中
        activityInfoList.stream().forEach(item ->{
            item.setActivityTypeString(item.getActivityType().getComment());
        });
        return activityInfoPage;
    }

    //根据活动id获取活动规则数据
    @Override
    public Map<String, Object> findActivityRuleList(Long id) {

        LambdaQueryWrapper<ActivityRule> activityRuleWrapper = new LambdaQueryWrapper<>();
        activityRuleWrapper.eq(ActivityRule::getActivityId,id);
        List<ActivityRule> activityRuleList = activityRuleMapper.selectList(activityRuleWrapper);

        LambdaQueryWrapper<ActivitySku> activitySkuWrapper = new LambdaQueryWrapper<>();
        activitySkuWrapper.eq(ActivitySku::getActivityId,id);
        List<ActivitySku> activitySkuList = activitySkuMapper.selectList(activitySkuWrapper);
        List<Long> skuIdList = activitySkuList.stream()
                .map(ActivitySku::getSkuId)
                .collect(Collectors.toList());

        //远程调用,根据商品skuId集合,批量获取商品信息
        List<SkuInfo> skuInfoList = productFeignClient.findSkuInfoList(skuIdList);

        Map<String, Object> map = new HashMap<>();
        map.put("activityRuleList",activityRuleList);
        map.put("skuInfoList",skuInfoList);

        return map;
    }

    //新增活动规则
    @Override
    public void saveActivityRule(ActivityRuleVo activityRuleVo) {
        //根据活动id删除之前的活动规则数据
        Long activityId = activityRuleVo.getActivityId();
        activityRuleMapper.delete(new LambdaQueryWrapper<ActivityRule>().eq(ActivityRule::getActivityId,activityId));
        activitySkuMapper.delete(new LambdaQueryWrapper<ActivitySku>().eq(ActivitySku::getActivityId,activityId));

        //添加新的活动规则数据,并封装相关信息
        List<ActivityRule> activityRuleList = activityRuleVo.getActivityRuleList();
        ActivityInfo activityInfo = baseMapper.selectById(activityId);

        for(ActivityRule activityRule : activityRuleList){
            activityRule.setActivityId(activityId);
            activityRule.setActivityType(activityInfo.getActivityType());
            activityRuleMapper.insert(activityRule);
        }

        List<ActivitySku> activitySkuList = activityRuleVo.getActivitySkuList();
        for(ActivitySku activitySku : activitySkuList){
            activitySku.setActivityId(activityId);
            activitySkuMapper.insert(activitySku);
        }
    }

    //根据关键字获取sku列表，活动使用
    @Override
    public List<SkuInfo> findSkuInfoByKeyword(String keyword) {
        //1.远程调用,根据关键字查询商品信息表,得到skuInfo列表
        List<SkuInfo> skuInfoList = productFeignClient.findSkuInfoByKeyword(keyword);
            //如果根据关键字查不到匹配内容,直接返回空集合
            if(skuInfoList.size() == 0){
                return skuInfoList;
            }

        //2.根据skuInfoList 得到skuIdList
        List<Long> skuIdList = skuInfoList.stream().map(SkuInfo::getId).collect(Collectors.toList());

        //3.根据商品skuId的集合,查询activity_sku表 和 activity_info表,查出已经参加了活动,且活动没有过期的商品skuId的集合
        //需要写sql语句
        List<Long> existSkuIdList = baseMapper.selectExistSkuIdList(skuIdList);

        //新建一个集合,将最终筛选得到的商品加入其中
        List<SkuInfo> finalSkuInfoList = new ArrayList<>();
            //遍历所有商品,如果商品skuId没有包含在existSkuIdList中,则为没有参加过活动的商品,加入到最终要返回的集合中
            for(SkuInfo skuInfo : skuInfoList){
                if(!existSkuIdList.contains(skuInfo.getId())){
                    finalSkuInfoList.add(skuInfo);
                }
            }
        return finalSkuInfoList;
    }

    //根据skuId列表获取促销信息
    @Override
    public Map<Long, List<String>> findActivity(List<Long> skuIdList) {
        Map<Long, List<String>> result = new HashMap<>();
        //skuIdList遍历，得到每个skuId
        skuIdList.forEach(skuId -> {
            //根据skuId进行查询，查询sku对应活动里面规则列表
            List<ActivityRule> activityRuleList =
                    activityRuleMapper.findActivityRule(skuId);
            //数据封装，规则名称
            if(!CollectionUtils.isEmpty(activityRuleList)) {
                List<String> ruleList = new ArrayList<>();
                //把规则名称处理
                for (ActivityRule activityRule:activityRuleList) {
                    String ruleDesc = this.getRuleDesc(activityRule);
                    ruleList.add(ruleDesc);
                }
                result.put(skuId,ruleList);
            }
        });
        return result;
    }

    //根据skuId,userId,获取促销与优惠券信息
    @Override
    public Map<String, Object> findActivityAndCoupon(Long skuId, Long userId) {

        //根据skuId,获取商品促销信息
        // 一个sku只能有一个促销活动，一个活动有多个活动规则,如满赠，满100送10，满500送50)
        List<ActivityRule> activityRuleList = activityRuleMapper.findActivityRule(skuId);
        //遍历促销规则集合,调用方法getRuleDesc把规则名称处理,然后封装
        if(!CollectionUtils.isEmpty(activityRuleList)) {
            for (ActivityRule activityRule : activityRuleList) {
                String ruleDesc = this.getRuleDesc(activityRule);
                activityRule.setRuleDesc(ruleDesc);
            }
        }
        //根据skuId,userId,categoryId,获取优惠券信息
        Long categoryId = productFeignClient.getCategoryIdBySkuId(skuId);
        List<CouponInfo> couponInfoList = couponInfoMapper.findCouponInfo(skuId,userId,categoryId);

        Map<String, Object> map = new HashMap<>();
        map.put("activityRuleList", activityRuleList);
        map.put("couponInfoList", couponInfoList);
        return map;
    }

    //获取购物车满足条件的促销与优惠券信息
    @Override
    public OrderConfirmVo findCartActivityAndCoupon(List<CartInfo> cartInfoList, Long userId) {
        //购物车商品项cartInfoList中商品sku的信息,得到这此商品满足的优惠活动,该活动信息封装在类CartInfoVo中
        List<CartInfoVo> carInfoVoList = this.findCartActivityList(cartInfoList);
        //促销活动优惠的总金额
        BigDecimal activityReduceAmount = carInfoVoList.stream()
                .filter(carInfoVo -> null != carInfoVo.getActivityRule())
                .map(carInfoVo -> carInfoVo.getActivityRule().getReduceAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        //购物车可使用的优惠券列表
        List<CouponInfo> couponInfoList = couponInfoService.findCartCouponInfo(cartInfoList, userId);

        //优惠券可优惠的总金额，一次购物只能使用一张优惠券
        BigDecimal couponReduceAmount = new BigDecimal(0);
        if(!CollectionUtils.isEmpty(couponInfoList)) {
            couponReduceAmount = couponInfoList.stream()
                    .filter(couponInfo -> couponInfo.getIsOptimal().intValue() == 1)
                    .map(couponInfo -> couponInfo.getAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        //购物车原始总金额
        BigDecimal originalTotalAmount = cartInfoList.stream()
                .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                .map(cartInfo -> cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        //最终总金额 = 购物车原始总金额-促销活动优惠的总金额-优惠券可优惠的总金额
        BigDecimal totalAmount = originalTotalAmount.subtract(activityReduceAmount).subtract(couponReduceAmount);

        OrderConfirmVo orderTradeVo = new OrderConfirmVo();
        orderTradeVo.setCarInfoVoList(carInfoVoList);
        orderTradeVo.setActivityReduceAmount(activityReduceAmount);
        orderTradeVo.setCouponInfoList(couponInfoList);
        orderTradeVo.setCouponReduceAmount(couponReduceAmount);
        orderTradeVo.setOriginalTotalAmount(originalTotalAmount);
        orderTradeVo.setTotalAmount(totalAmount);
        return orderTradeVo;
    }

    //购物车商品项cartInfoList 中商品sku的信息,得到这此商品满足的优惠活动,该活动信息封装在类CartInfoVo中
    public List<CartInfoVo> findCartActivityList(List<CartInfo> cartInfoList) {
        List<CartInfoVo> carInfoVoList = new ArrayList<>();
        //第一步：把购物车里面相同活动的购物项汇总一起
            //获取skuId列表
        List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).collect(Collectors.toList());
            //获取skuId列表对应的全部促销规则(activity_sku,activity_info)
        List<ActivitySku> activitySkuList = baseMapper.selectCartActivityList(skuIdList);
            //根据活动id分组，取活动对应的skuId列表，即把购物车里面相同活动的购物项汇总一起，凑单使用
            //Map<Long, Set<Long>>  -> Long:activityId,Set<Long>:skuId的集合,同一个商品只能参加一种活动,所以skuId不能重复,使用Set
        Map<Long, Set<Long>> activityIdToSkuIdListMap = activitySkuList.stream()
                        .collect(Collectors.groupingBy(ActivitySku::getActivityId,
                                                       Collectors.mapping(ActivitySku::getSkuId, Collectors.toSet())));

        //第二步：获取活动对应的促销规则
            //skuId列表对应的全部促销规则(activitySkuList),根据此规则分解出对应的活动id集合
        Set<Long> activityIdSet = activitySkuList.stream().map(ActivitySku::getActivityId).collect(Collectors.toSet());
            //新建一个Map,key:activityId,value:activityRuleList
        Map<Long, List<ActivityRule>> activityIdToActivityRuleListMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(activityIdSet)) {
            //根据activityId的集合,查询对应的活动规则(表activity_rule),即购物车中商品项对应的所有活动 --> 满足条件的规则
            LambdaQueryWrapper<ActivityRule> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(ActivityRule::getActivityId, activityIdSet)
                        //重点*按满减金额顺序排列,例:满10减1,满29减6,则满29减6的规则在List集合中排在前面
                        //这样排序,方便后面商品总金额与满减金额对比(见方法computeFullReduction),计算出最优的满减活动(List是按序排列的,商品总金额先与满减金额大的对比)
                        .orderByDesc(ActivityRule::getConditionAmount, ActivityRule::getConditionNum);
            List<ActivityRule> activityRuleList = activityRuleMapper.selectList(queryWrapper);
            //按活动Id分组，获取活动对应的规则
            activityIdToActivityRuleListMap = activityRuleList.stream()
                    .collect(Collectors.groupingBy(activityRule -> activityRule.getActivityId()));
        }

        //第三步：根据活动汇总商品项，相同活动的商品项为一组显示在页面，并且计算最优优惠金额
            //记录有活动的商品项skuId
        Set<Long> activitySkuIdSet = new HashSet<>();
            //activityIdToSkuIdListMap, key(Long):activityId,value[Set<Long>]:参加活动员的skuId的集合
        if(!CollectionUtils.isEmpty(activityIdToSkuIdListMap)) {
            Iterator<Map.Entry<Long, Set<Long>>> iterator = activityIdToSkuIdListMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Long, Set<Long>> entry = iterator.next();
                Long activityId = entry.getKey();
                //当前活动对应的商品项skuId列表,currentActivitySkuIdSet为参加活动的商品项skuId列表
                Set<Long> currentActivitySkuIdSet = entry.getValue();
                //当前活动商品项中(cartInfoList),通过stream流,分解出对应的商品项列表集合
                List<CartInfo> currentActivityCartInfoList = cartInfoList.stream().
                        filter(cartInfo -> currentActivitySkuIdSet.contains(cartInfo.getSkuId()))
                                                                  .collect(Collectors.toList());
                //当前活动的总金额
                BigDecimal activityTotalAmount = this.computeTotalAmount(currentActivityCartInfoList);
                //当前活动的购物项总个数
                Integer activityTotalNum = this.computeCartNum(currentActivityCartInfoList);
                //activityIdToActivityRuleListMap(活动id对应的活动规则Map)
                //传入活动id,得到当前活动规则集合
                List<ActivityRule> currentActivityRuleList = activityIdToActivityRuleListMap.get(activityId);
                //当前活动规则 -> 活动类型getActivityType(1:满减,2:折扣),由于当前活动规则都是同一种活动的,所有每一条规则都有相同的活动类型
                ActivityType activityType = currentActivityRuleList.get(0).getActivityType();
                ActivityRule optimalActivityRule = null;
                if (activityType == ActivityType.FULL_REDUCTION) {
                    //满减最优规则,根据当前活动总金额 和 当前活动规则集合,得到满减最优规则
                    optimalActivityRule = this.computeFullReduction(activityTotalAmount, currentActivityRuleList);
                } else {
                    //满量最优规则,根据当前活动总金额,总数量 和 当前活动规则集合,得到满量最优规则
                    optimalActivityRule = this.computeFullDiscount(activityTotalNum, activityTotalAmount, currentActivityRuleList);
                }
                //同一活动对应的商品项列表与对应优化规则
                CartInfoVo carInfoVo = new CartInfoVo();
                carInfoVo.setCartInfoList(currentActivityCartInfoList);
                carInfoVo.setActivityRule(optimalActivityRule);
                carInfoVoList.add(carInfoVo);

                //记录,currentActivitySkuIdSet为参加活动的skuId集合
                //方便后面分解出没有活动的skuId的集合
                activitySkuIdSet.addAll(currentActivitySkuIdSet);
            }
        }
        //第四步：无活动的购物项，每一项一组
            //从所有商品项skuId集合中,减去参加活动的,就是没有参加活动的
            //减去后,skuIdList就是无活动的购物项skuId集合了
        skuIdList.removeAll(activitySkuIdSet);
        if(!CollectionUtils.isEmpty(skuIdList)) {
            //获取无活动skuId对应的商品项,skuIdToCartInfoMap  key->skuId,value->CartInfo
            Map<Long, CartInfo> skuIdToCartInfoMap = cartInfoList.stream().
                    collect(Collectors.toMap(CartInfo::getSkuId, CartInfo->CartInfo));
            for(Long skuId : skuIdList) {
                CartInfoVo carInfoVo = new CartInfoVo();
                carInfoVo.setActivityRule(null);
                List<CartInfo> currentCartInfoList = new ArrayList<>();
                currentCartInfoList.add(skuIdToCartInfoMap.get(skuId));
                carInfoVo.setCartInfoList(currentCartInfoList);
                carInfoVoList.add(carInfoVo);
            }
        }
        return carInfoVoList;
    }

    //根据活动商品项,得到活动总金额
    private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
        BigDecimal total = new BigDecimal("0");
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                total = total.add(itemTotal);
            }
        }
        return total;
    }

    //根据活动商品项集合,得到商品项数量
    private int computeCartNum(List<CartInfo> cartInfoList) {
        int total = 0;
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                total += cartInfo.getSkuNum();
            }
        }
        return total;
    }

    /**
     * 计算满减最优规则
     * @param totalAmount
     * @param activityRuleList //该活动规则skuActivityRuleList数据，已经按照优惠金额从大到小排序了
     */
    private ActivityRule computeFullReduction(BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
        ActivityRule optimalActivityRule = null;
        //该活动规则ActivityRuleList数据，已经按照满减金额从大到小排序了,例:满10减2,满29减6,则满29减6的规则排在List集合前面
        //这样,商品总金额先与满减金额大的对比,计算出最优的满减活动
        for (ActivityRule activityRule : activityRuleList) {
            //如果订单项金额大于等于满减金额，则优惠金额
            if (totalAmount.compareTo(activityRule.getConditionAmount()) > -1) {
                //优惠后减少金额
                activityRule.setReduceAmount(activityRule.getBenefitAmount());
                optimalActivityRule = activityRule;
                break;
            }
        }
        if(null == optimalActivityRule) {
            //如果没有满足条件的取最小满足条件的一项
            optimalActivityRule = activityRuleList.get(activityRuleList.size()-1);
            optimalActivityRule.setReduceAmount(new BigDecimal("0"));
            optimalActivityRule.setSelectType(1);

            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元，还差")
                    .append(totalAmount.subtract(optimalActivityRule.getConditionAmount()))
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
        } else {
            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元，已减")
                    .append(optimalActivityRule.getReduceAmount())
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
            optimalActivityRule.setSelectType(2);
        }
        return optimalActivityRule;
    }

    /**
     * 计算满量打折最优规则
     * @param totalNum
     * @param activityRuleList //该活动规则skuActivityRuleList数据，已经按照优惠折扣从大到小排序了
     */
    private ActivityRule computeFullDiscount(Integer totalNum, BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
        ActivityRule optimalActivityRule = null;
        //将规则ActivityRuleList数据，按优惠折扣属性升序排列,方便得到最低折扣
        List<ActivityRule> activityRuleDescByBenefitDiscountList = activityRuleList.stream()
                .sorted(Comparator.comparing(ActivityRule::getBenefitDiscount))
                .collect(Collectors.toList());
        for (ActivityRule activityRule : activityRuleDescByBenefitDiscountList) {
            //如果订单项购买个数大于等于满减件数，则优化打折
            if (totalNum.intValue() >= activityRule.getConditionNum()) {
                BigDecimal skuDiscountTotalAmount = totalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                BigDecimal reduceAmount = totalAmount.subtract(skuDiscountTotalAmount);
                activityRule.setReduceAmount(reduceAmount);
                optimalActivityRule = activityRule;
                break;
            }
        }
        if(null == optimalActivityRule) {
            //如果没有满足条件的取最小满足条件的一项
            optimalActivityRule = activityRuleList.get(activityRuleList.size()-1);
            optimalActivityRule.setReduceAmount(new BigDecimal("0"));
            optimalActivityRule.setSelectType(1);

            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionNum())
                    .append("件打")
                    .append(optimalActivityRule.getBenefitDiscount())
                    .append("折，还差")
                    .append(totalNum-optimalActivityRule.getConditionNum())
                    .append("件");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
        } else {
            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionNum())
                    .append("件打")
                    .append(optimalActivityRule.getBenefitDiscount())
                    .append("折，已减")
                    .append(optimalActivityRule.getReduceAmount())
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
            optimalActivityRule.setSelectType(2);
        }
        return optimalActivityRule;
    }

    //构造规则名称的方法
    private String getRuleDesc(ActivityRule activityRule) {
        ActivityType activityType = activityRule.getActivityType();
        StringBuffer ruleDesc = new StringBuffer();
        if (activityType == ActivityType.FULL_REDUCTION) {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionAmount())
                    .append("元减")
                    .append(activityRule.getBenefitAmount())
                    .append("元");
        } else {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionNum())
                    .append("件打")
                    .append(activityRule.getBenefitDiscount())
                    .append("折");
        }
        return ruleDesc.toString();
    }
}
